Conversation
Add server-side logging and richer error details for app submissions, and client-side authentication validation and debug logs. In api/submissions.ts the handler now logs inserted submission payloads and returns error details when available. In AppSubmissionForm.tsx the form validates that user id/email exist before submitting, uses non-null user fields, and adds console.error logs for submission and network failures to aid debugging.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR adds a String UI design system + reusable component library, refactors the main app/dashboard/profile UIs around those components (including new swipe interactions on mobile), and introduces a local Express-based dev API server with Vite proxying to improve local development.
Changes:
- Introduces a documented design system (
STYLING.md) and a set of reusable UI components (Button/Card/Modal/etc.), then refactors profile + dashboard screens to use them. - Replaces long-press interactions with swipe gestures for pin/unpin and adds a “Submit App” modal flow from both homepage and dashboard.
- Adds a local dev API server (
server/dev-server.js) and updates tooling/config (Vite proxy, npm scripts, Vercel rewrites/headers, favicon/touch icons).
Reviewed changes
Copilot reviewed 33 out of 72 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| vite.config.ts | Proxies /api to the local dev API server for local development. |
| vercel.json | Adds caching headers for icons and refines SPA rewrites to avoid rewriting static assets. |
| src/lib/auth-client.ts | Simplifies user-save API call to always use /api/users and adjusts logging. |
| src/hooks/useSwipe.ts | New swipe-gesture hook for mobile interactions. |
| src/db/schema.ts | Updates user ID types to text, removes some FKs, and adds submission analytics counters. |
| src/components/ui/PinButton.tsx | New reusable pin/unpin icon button component. |
| src/components/ui/Modal.tsx | New modal component used for “Submit App” flows. |
| src/components/ui/LaunchButton.tsx | New reusable “open in new tab” icon link component. |
| src/components/ui/IconButton.tsx | New generic icon button component. |
| src/components/ui/Header.tsx | New header component for branded top navigation. |
| src/components/ui/DashboardHeader.tsx | New dashboard-specific header with tabs, theme toggle, and user menu. |
| src/components/ui/Card.tsx | New card wrapper component for consistent layout/styling. |
| src/components/ui/Button.tsx | New shared button component implementing design-system variants/sizes. |
| src/components/ui/AppCard.tsx | New app display card used in app lists/grids. |
| src/components/profile/ProfileHeader.tsx | New profile header block for personal profile pages. |
| src/components/profile/ProfileFooter.tsx | New branded footer for profile pages. |
| src/components/profile/AppsList.tsx | New apps list/grid with contribution-first sorting. |
| src/components/dashboard/MySubmissions.tsx | New dashboard submissions view (desktop table + mobile cards). |
| src/components/UserDashboard.tsx | Refactors dashboard to use new header, modal submission flow, and MySubmissions component. |
| src/components/Router.tsx | Routes profile pages to a dev mock in local development. |
| src/components/ProfileModal.tsx | Adds profile-as-modal component (currently unused). |
| src/components/PersonalProfile.tsx | Refactors personal profile view to use new profile components. |
| src/components/Footer.tsx | Renames footer links (“Privacy Policy”→“Privacy”, “Terms & Conditions”→“Terms”). |
| src/components/DevProfileMock.tsx | Adds a local-development mock profile page for testing without backend dependencies. |
| src/components/AuthButton.tsx | Adds authenticated dropdown menu actions (“My Launcher”, dashboard, sign out). |
| src/components/AppSubmissionForm.tsx | Adds app autocomplete, improved messaging, and onSuccess callback support for modal usage. |
| src/App.tsx | Integrates new UI components, swipe interactions, and “Submit App” modal from the homepage. |
| server/dev-server.js | Adds local Express dev API server for /api/users and /api/submissions. |
| public/apple-touch-icon-iphone-retina-120x120.png | Adds Apple touch icon asset. |
| public/apple-touch-icon-iphone-60x60.png | Adds Apple touch icon asset. |
| public/apple-touch-icon-ipad-retina-152x152.png | Adds Apple touch icon asset. |
| public/apple-touch-icon-ipad-76x76.png | Adds Apple touch icon asset. |
| package.json | Adds concurrently and scripts to run client+dev API together. |
| package-lock.json | Locks new dependencies introduced by concurrently. |
| index.html | Updates theme color and adds favicon/touch icon/meta tags. |
| claude.md | Updates project status/architecture documentation. |
| api/submissions.ts | Enhances submissions API with user existence checks and richer error responses. |
| STYLING.md | New design system documentation (colors, patterns, layout standards, theming). |
| .gitignore | Ignores .env*.local files. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| app.get('/api/submissions', async (req, res) => { | ||
| try { | ||
| const userId = req.query.userId; | ||
|
|
||
| if (!userId) { | ||
| return res.status(400).json({ error: 'Missing userId parameter' }); | ||
| } | ||
|
|
There was a problem hiding this comment.
The /api/submissions GET handler requires userId in the query string, but current frontend calls don't send it. Either relax this in dev (e.g., infer from auth in headers / accept missing param) or update frontend to always pass userId so local dev matches production behavior.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| } catch (error) { | ||
| console.error('Database error:', error); | ||
| return new Response( | ||
| JSON.stringify({ error: 'Internal server error' }), | ||
| JSON.stringify({ | ||
| error: 'Internal server error', | ||
| details: error instanceof Error ? error.message : 'Unknown error' | ||
| }), |
There was a problem hiding this comment.
The 500 response includes details derived from error.message. In production this can leak internal/database details to clients. Consider logging the detailed error server-side only and returning a generic error response (or gate details behind a development flag).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| <a href="/" className="text-string-mint hover:text-string-mint-light font-medium transition-colors"> | ||
| <img | ||
| src="/Brand Guidelines/4. Svg Separate Files/primary_dark.svg" | ||
| alt="String" | ||
| className="h-4 inline" | ||
| /> |
There was a problem hiding this comment.
This image path points to /Brand Guidelines/..., but that folder is not part of the Vite public/ assets and won't be available in the built app (404 in production). Copy the required SVG into public/ (e.g. public/logo-dark.svg) and reference it from there, or import it via the bundler.
|
|
||
| console.log('AuthButton render - user:', user, 'isAuthenticated:', isAuthenticated, 'isLoading:', isLoading); | ||
|
|
There was a problem hiding this comment.
There is a console.log in the render path. This will spam production logs and can leak user info (email) to the console. Consider removing it or gating behind a development-only check.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| import { useState, useEffect } from 'react'; | ||
| import { PersonalProfile } from './PersonalProfile'; |
There was a problem hiding this comment.
PersonalProfile is imported but never used in this file. With noUnusedLocals: true in tsconfig.json, this will fail TypeScript builds. Remove the unused import (or use it instead of duplicating profile rendering here).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| const loadSubmissions = async () => { | ||
| try { | ||
| setLoading(true); | ||
| const response = await fetch('/api/submissions'); | ||
| if (response.ok) { | ||
| const data = await response.json(); | ||
| setSubmissions(data.submissions); | ||
| } |
There was a problem hiding this comment.
GET /api/submissions requires a userId query parameter (see both api/submissions.ts and server/dev-server.js), but this fetch calls /api/submissions without it. This will always 400 and the submissions list will never load. Consider pulling userId from useAuth() here (or pass it in as a prop) and request /api/submissions?userId=....
| // Ensure user exists in database before creating submission | ||
| const existingUser = await db | ||
| .select() | ||
| .from(users) | ||
| .where(eq(users.id, submittedByUserId)) | ||
| .limit(1); | ||
|
|
||
| if (existingUser.length === 0) { | ||
| // Create user if doesn't exist | ||
| await db.insert(users).values({ | ||
| id: submittedByUserId, | ||
| email: submittedByEmail, | ||
| name: null, | ||
| slug: null, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Creating a placeholder user with slug: null can permanently break profile routing: api/users.ts only generates a slug when the user does not already exist, and does not backfill a missing slug on updates. If a user is inserted here first, they may never get a slug. Suggest reusing the same slug-generation/upsert logic as api/users.ts (or update existing users where slug IS NULL).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| return ( | ||
| <div className="fixed inset-0 z-50 overflow-y-auto"> | ||
| {/* Backdrop */} | ||
| <div | ||
| className="fixed inset-0 bg-black bg-opacity-50 transition-opacity" | ||
| onClick={onClose} | ||
| /> | ||
|
|
||
| {/* Modal */} | ||
| <div className="flex min-h-full items-center justify-center p-4"> | ||
| <div className={`relative w-full ${sizeClasses[size]} bg-white rounded-xl shadow-xl`}> | ||
| {/* Header */} | ||
| <div className="flex items-center justify-between p-6 border-b border-gray-200"> | ||
| <h3 className="text-lg font-semibold text-string-dark">{title}</h3> | ||
| <button | ||
| onClick={onClose} | ||
| className="p-1 rounded-lg text-gray-400 hover:text-gray-600 hover:bg-gray-100 transition-colors" | ||
| > | ||
| <svg className="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | ||
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" /> | ||
| </svg> | ||
| </button> |
There was a problem hiding this comment.
The modal is missing key accessibility semantics and focus management (e.g., role="dialog", aria-modal="true", aria-labelledby, and trapping focus / returning focus to the trigger). Without these, keyboard and screen-reader users can have a broken experience. Consider adding the ARIA attributes at minimum, and ideally a focus trap (or use an accessible dialog primitive).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| 'bg-white border border-gray-100 hover:border-string-mint hover:shadow-sm', | ||
| 'bg-[#2a2d30] border border-[#3a3f44] hover:border-string-mint' | ||
| )} ${swipeProps.isSwipeMenuOpen ? 'transform -translate-x-20' : ''}`} | ||
| {...swipeProps} | ||
| > |
There was a problem hiding this comment.
Same issue here: spreading the full useSwipe() return object onto a <div> passes non-standard props to the DOM. Only spread the touch/click handlers (or a bind object) and keep state values separate.
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
| app.use(cors()); | ||
| app.use(express.json()); | ||
|
|
||
| const connectionString = process.env.DATABASE_URL; | ||
| if (!connectionString) { | ||
| console.error('DATABASE_URL environment variable is not set'); | ||
| process.exit(1); | ||
| } | ||
|
|
||
| const sqlClient = neon(connectionString); | ||
| const db = drizzle(sqlClient); | ||
|
|
||
| // POST /api/users | ||
| app.post('/api/users', async (req, res) => { | ||
| try { | ||
| const { id, email, name, image, provider } = req.body; | ||
| if (!id || !email) { | ||
| return res.status(400).json({ error: 'id and email are required' }); | ||
| } | ||
|
|
||
| const slug = generateSlugFromEmail(email); |
There was a problem hiding this comment.
This dev server doesn't implement GET /api/apps, but the client fetches /api/apps (App.tsx and AppSubmissionForm.tsx). With the Vite /api proxy pointing to this server, local dev will 404 for the app directory and autocomplete. Add a GET /api/apps route here (or adjust the proxy to exclude /api/apps).
There was a problem hiding this comment.
@copilot open a new pull request to apply changes based on this feedback
|
@ghostleek I've opened a new pull request, #15, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #16, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #17, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #18, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #19, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #20, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #21, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@ghostleek I've opened a new pull request, #22, to work on those changes. Once the pull request is ready, I'll request review from you. |
This pull request introduces a comprehensive UI design system for the String app, refines the app's component architecture, and improves the development workflow and API error handling. Key highlights include the addition of a detailed design system documentation, new reusable UI components, enhanced mobile interactions, and a dedicated dev API server for local development.
Design System & UI Architecture
STYLING.mdto document the new String Design System, specifying brand color palette, component patterns, layout standards, and theme support.claude.mdto reflect Phase 4 completion, listing the design system, component library (Button, Card, AppCard, Header), and component architecture for better dev/prod consistency. [1] [2] [3] [4] [5] [6]Component & Interaction Enhancements
src/App.tsxto use new reusable UI components and replaced long-press interactions with swipe gestures for mobile (usinguseSwipeinstead ofuseLongPress), improving mobile UX for pin/unpin actions. [1] [2] [3] [4] [5] [6]Development Workflow Improvements
server/dev-server.js) using Express for local development, with endpoints for app submissions and user-specific queries.package.jsonscripts to support concurrent running of client and API servers for a smoother dev experience, and addedconcurrentlyas a dependency. [1] [2]API & Error Handling
api/submissions.tsto include error details, and added logging for better debugging. [1] [2]Branding & Meta Updates
index.htmlto use the new brand color (#33373B) for theme and tile colors, and added a comprehensive set of favicons and Apple touch icons.